using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Ink;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Data;
using System.ComponentModel;
using System.Windows.Documents;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Windows.Media.Imaging;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

using Microsoft.Win32;

using WozLib.UI.Ink.SketchProperties;
using WozLib.Net;
using WozLib.UI.Ink.SketchElements;
using WozLib;
using WozLib.UI.Ink;
using WozLib.UI.Ink.SketchCommands;

using WozLib.UI.Adorners;
using WozLib.WozCommands;
using WozLib.UI.Ink.SketchObjects;
using WozLib.UI.Ink.Adorners;
using WozLib.WozObjects;

namespace WozLib.UI.Ink
{
  // If trying to show this in a UI you can always use a container object 
  // that just uses this UIElement as its child
  public class SketchPad : SketchInkCanvas, IServerObject, INotifyPropertyChanged
  {

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void FirePropertyChanged(string propertyName)
    {
      if (this.PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

    #endregion

    private SketchCommandReferences _DefaultWozClientCommandReferences;

    /// <summary>
    /// Possible "InkTools" or really tools for sketching on this SketchPad
    /// </summary>
    public enum InkTools {Pencil,Eraser,Selector,Line,Rectangle,Ellipse,Image,Text,Button,Arrow,Gradient,Gesture,Link,None}

    /// <summary>
    /// Delegate for being notified when all SketchPads change tool
    /// </summary>
    /// <param name="currentInkTool">The tool all SketchPads are now using</param>
    public delegate void CurrentInkToolChangedDelegate(InkTools currentInkTool);

    /// <summary>
    /// Occurs when all SketchPads change tools
    /// </summary>
    public static event CurrentInkToolChangedDelegate CurrentInkToolChanged;    

    public static InkTools CurrentInkTool
    {
      get
      {
        return _CurrentInkTool;
      }
      set
      {
        _CurrentInkTool = value;
        if (CurrentInkToolChanged != null)
        {
          CurrentInkToolChanged(value);
        }
      }
    } static InkTools _CurrentInkTool = InkTools.Pencil;

    public string Description
    {
      get { return (string)GetValue(DescriptionProperty); }
      set
      {
        SetValue(DescriptionProperty, value);
        FirePropertyChanged("Description");
      }
    }

    // Using a DependencyProperty as the backing store for Description.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DescriptionProperty =
        DependencyProperty.Register("Description", typeof(string), typeof(SketchPad), new UIPropertyMetadata("", new PropertyChangedCallback(SomePropertyChanged)));


    //public string Description
    //{
    //  get { return _Description; }
    //  set
    //  {
    //    // Broke this for undo/redo
    //    // create erase command
    //    //WozScreenChangeNameCommand screenChangeNameCommand = new WozScreenChangeNameCommand(ServerID,_Description,value);

    //    // push erase command
    //    //NotifyCommandOccured(screenChangeNameCommand);

    //    _Description = value;
    //    FirePropertyChanged("Description");
    //  }
    //} private string _Description;

    public Stack<WozCommand> SinceCommitServerCommandStack
    {
      get { return _SinceCommitServerCommandStack; }
      set { _SinceCommitServerCommandStack = value; }
    } private Stack<WozCommand> _SinceCommitServerCommandStack = new Stack<WozCommand>();

    public bool InOverlayMode
    {
      get { return _InOverlayMode; }
      set
      {
        _InOverlayMode = value;

        
        // if we are in overlay mode we need to hide the hourglass
        if (value)
        {
          // say put up hourglass and set input mode to none
          HideHourglassSketchCommand HideHourglassSketchCommand = new HideHourglassSketchCommand();
          NotifyNoUndoRedoCommandOccured(new ClientWozCommand(HideHourglassSketchCommand));
        }
        else // if we are not in overlay mode we need to hide our overlay visual
             // we also put up an hourglass
        {

          //if (InkOverlayEllipse != null)
          //{
          //  InkOverlayEllipse.Visibility = Visibility.Hidden;
          //}

          // hide mouse overlay
          if (_ClientMouseAdorner != null)
          {
            _ClientMouseAdorner.Visibility = Visibility.Hidden;
          }

          // say put up hourglass and set input mode to none
          ShowHourglassSketchCommand showHourglassSketchCommand = new ShowHourglassSketchCommand();
          NotifyNoUndoRedoCommandOccured(new ClientWozCommand(showHourglassSketchCommand));
        }
      }
    } private bool _InOverlayMode = false;


    public bool Suspended
    {
      get { return _Suspended; }
      set { _Suspended = value; }
    } private bool _Suspended;


    // This is our reference to all of our WozObjects.
    // We use our own registry so that we can create a duplicate object space from the client mirror
    // then discard or overwrite what the client has done.
    public SketchObjectRegistry WozObjectRegistry
    {
      get { return _WozObjectRegistry; }
      set { _WozObjectRegistry = value; }
    } SketchObjectRegistry _WozObjectRegistry;

    private List<SketchObject> _PreviousSelectedWozObjects = new List<SketchObject>();

    //public double InternalWidth
    //{
    //  get
    //  {
    //    return _InternalWidth;
    //  }
    //  set
    //  {
    //    _InternalWidth = value;
    //  }
    //} double _InternalWidth;

    public static readonly DependencyProperty InternalWidthProperty;

    public static readonly DependencyProperty InternalHeightProperty;

    /// <summary>
    /// Monitors dependency properties for changes and sends
    /// WozCommands when they change.
    /// </summary>
    /// <param name="wozObjectContainer"></param>
    /// <param name="args"></param>
    static void SomePropertyChanged(DependencyObject wozObjectContainer, DependencyPropertyChangedEventArgs args)
    {
      if (wozObjectContainer is SketchPad)
      {
        SketchPad iWozObjectContainer = wozObjectContainer as SketchPad;

        // send update of our width
        if (args.Property == InternalWidthProperty)
        {
          WindowResizeWozCommand wozWindowResizeCommand = new WindowResizeWozCommand(new Size((double)args.NewValue, iWozObjectContainer.Height), new Size((double)args.OldValue, iWozObjectContainer.Height));
          iWozObjectContainer.NotifyCommandOccured(wozWindowResizeCommand);
        }

        // send update of our height
        if (args.Property == InternalHeightProperty)
        {
          WindowResizeWozCommand wozWindowResizeCommand = new WindowResizeWozCommand(new Size(iWozObjectContainer.Width, (double)args.NewValue), new Size(iWozObjectContainer.Width, (double)args.OldValue));
          iWozObjectContainer.NotifyCommandOccured(wozWindowResizeCommand);
        }

        // send update of our WindowTitle (description)
        if (args.Property == DescriptionProperty)
        {
          WindowRenameSketchCommand windowRenameSketchCommand = new WindowRenameSketchCommand(args.NewValue as string, args.OldValue as string);
          iWozObjectContainer.NotifyCommandOccured(windowRenameSketchCommand);
        }
      }
    }

    // NOT THREAD SAFE
    public List<SketchObject> SelectedWozObjects
    {
      get
      {
        List<SketchObject> selectedWozObjects = new List<SketchObject>();


        foreach (UIElement uie in this.GetSelectedElements())
        {
          if (uie is ISketchObject)
          {
            selectedWozObjects.Add((uie as ISketchObject).GetWozObject());
          }
        }


        foreach (Stroke stroke in this.GetSelectedStrokes())
        {
          if (stroke is ISketchObject)
          {
            selectedWozObjects.Add((stroke as ISketchObject).GetWozObject());
          }
        }

        return selectedWozObjects;
      }
    }

    // NOT THREAD SAFE
    // almost same as GetWozObjectList
    public IEnumerable<SketchObject> VisibleWozObjects
    {
      get
      {
        List<SketchObject> wozObjectOrder = new List<SketchObject>();

        foreach (UIElement uie in this.Children)
        {
          if (uie is ISketchObject)
          {
            wozObjectOrder.Add((uie as ISketchObject).GetWozObject());
          }
        }

        foreach (Stroke stroke in this.Strokes)
        {
          if (stroke is ISketchObject)
          {
            wozObjectOrder.Add((stroke as ISketchObject).GetWozObject());
          }
        }

        return wozObjectOrder;
      }
    }

    // Perhaps this should really be IEnumerable
    public List<CascadingProperty> SelectWozObjectCascadingProperties
    {
      get
      {
        // Usually get's asked following a
        // FirePropertyChanged("SelectWozObjectCascadingProperties");

        // make a list of all selected IWozObject
        List<ISketchObject> iWozObjects = new List<ISketchObject>();

        foreach (Stroke stroke in this.GetSelectedStrokes())
        {
          if (stroke is ISketchObject)
          {
            ISketchObject iWozObject = stroke as ISketchObject;

            iWozObjects.Add(iWozObject);
          }
        }

        foreach (UIElement uie in this.GetSelectedElements())
        {
          if (uie is ISketchObject)
          {
            ISketchObject iWozObject = uie as ISketchObject;

            iWozObjects.Add(iWozObject);
          }
        }

        // Now figure out what properties they all have in common and are equal
        // using .Equals
        if (iWozObjects.Count > 0)
        {
          SketchPropertyCollection candidateProperties = new SketchPropertyCollection();

          // put the properties of this first iWozObject in the list
          ISketchObject iWozObjectZero = iWozObjects[0];
          foreach (SketchProperty wozProperty in iWozObjectZero.GetProperties())
          {
            if (wozProperty.WozPropertyType != WozPropertyType.Ink &&
              wozProperty.WozPropertyType != WozPropertyType.LineGeometry &&
              //Type != WozPropertyType.RectGeometry &&
              wozProperty.WozPropertyType != WozPropertyType.FillImageUrl &&
              wozProperty.WozPropertyType != WozPropertyType.Bounds)
            {
              candidateProperties.SetProperty(wozProperty.Clone() as SketchProperty);
            }
          }

          // find properties everyone else has in common (i know this does index 0 twice for no reason)
          foreach (ISketchObject iWozObject in iWozObjects)
          {
            //foreach (WozProperty wozProperty in iWozObject.GetProperties())
            //{
            //  //&& candidateProperties.GetProperty(Type).Equals(wozProperty)
            //  if (!
            //    (candidateProperties.HasProperty(Type)))
            //  {
            //    candidateProperties.RemoveProperty(Type);
            //  }
            //}
            foreach (SketchProperty wozProperty in candidateProperties.WozProperties)
            {
              if (iWozObject.GetProperty(wozProperty.WozPropertyType) == null)
              {
                candidateProperties.RemoveProperty(wozProperty.WozPropertyType);
              }
            }
          }

          // create a list of cascading properties based on what properties these IWozObjects have in common
          List<CascadingProperty> cascadingProperties = new List<CascadingProperty>();
          foreach (SketchProperty wozProperty in candidateProperties.WozProperties)
          {
            CascadingProperty cascadingProperty = wozProperty.CreateCascadingProperty(iWozObjects);
            cascadingProperty.CommandOccured += new CascadingProperty.CommandOccuredDelegate(cascadingProperty_CommandOccured);
            cascadingProperty.ContinuousCommandOccured += new CascadingProperty.CommandOccuredDelegate(cascadingProperty_ContinuousCommandOccured);
            cascadingProperties.Add(cascadingProperty);
          }

          return cascadingProperties;
        }
        else
        {
          return new List<CascadingProperty>();
        }
      }
    }

    void cascadingProperty_ContinuousCommandOccured(Ink.SketchCommands.SketchCommand wozClientCommand)
    {
      // this doens't make sense anymore, they should just have references to what they need to touch
      wozClientCommand.WozClientCommandReferences = _DefaultWozClientCommandReferences;
      ClientWozCommand wozServerClientCommand = new ClientWozCommand(wozClientCommand);

      // actually do command
      wozServerClientCommand.Redo();

      if (ContinuousCommandOccured != null)
      {
        ContinuousCommandOccured(wozServerClientCommand);
      }
    }

    void cascadingProperty_CommandOccured(Ink.SketchCommands.SketchCommand wozClientCommand)
    {
      wozClientCommand.WozClientCommandReferences = _DefaultWozClientCommandReferences;
      ClientWozCommand wozServerClientCommand = new ClientWozCommand(wozClientCommand);

      SketchPadWozCommand wocsc = new SketchPadWozCommand(this.ServerID, wozServerClientCommand, true);

      // actually do command
      wocsc.Redo();

      if (CommandOccured != null)
      {
        CommandOccured(wocsc);
      }
    }

    public void ApplyCommand(Ink.SketchCommands.SketchCommand wozClientCommand)
    {
      wozClientCommand.WozClientCommandReferences = _DefaultWozClientCommandReferences;
      ClientWozCommand wozServerClientCommand = new ClientWozCommand(wozClientCommand);

      SketchPadWozCommand wocsc = new SketchPadWozCommand(this.ServerID, wozServerClientCommand, true);

      // actually do command
      wocsc.Redo();

      if (CommandOccured != null)
      {
        CommandOccured(wocsc);
      }
    }

    private InkTools SelectedInkTool
    {
      get
      {
        return _SelectedInkTool;
      }
      set
      {
        if (IgnoreInkTool)
        {
          // we don't set it if we are ignoring ink tools
          _SelectedInkTool = InkTools.None;
        }
        else 
        {

          _SelectedInkTool = value;

          switch (_SelectedInkTool)
          {
            case InkTools.Pencil:
              this.UseCustomCursor = true;
              this.EditingMode = InkCanvasEditingMode.Ink;
              this.Cursor = Cursors.Pen;
              break;
            case InkTools.Eraser:
              this.UseCustomCursor = false;
              this.EditingMode = InkCanvasEditingMode.EraseByStroke;
              break;
            case InkTools.Selector:
              this.UseCustomCursor = false;
              this.EditingMode = InkCanvasEditingMode.Select;
              break;
            case InkTools.Line:
              goto case InkTools.Rectangle;
            case InkTools.Button:
              goto case InkTools.Rectangle;
            case InkTools.Ellipse:
              goto case InkTools.Rectangle;
            case InkTools.Image:
              goto case InkTools.Rectangle;
            case InkTools.Text:
              goto case InkTools.Rectangle;
            case InkTools.Arrow:
              goto case InkTools.Rectangle;
            case InkTools.Gradient:
              goto case InkTools.Rectangle;
            case InkTools.Link:
              goto case InkTools.Rectangle;
            case InkTools.Rectangle:
              this.UseCustomCursor = true;
              this.EditingMode = InkCanvasEditingMode.None;
              this.Cursor = Cursors.Cross;
              break;
            default:
              break;
          }
        }
      }
    } private InkTools _SelectedInkTool;

    
    /// <summary>
    /// Indicates whether this SketchPad should ignore ink tools
    /// </summary>
    public bool IgnoreInkTool
    {
      get { return _IgnoreInkTool; }
      set { 
        _IgnoreInkTool = value;
        if (value)
        {
          this.EditingMode = InkCanvasEditingMode.None;
          _SelectedInkTool = InkTools.None;
        }
      }
    } private bool _IgnoreInkTool = false;


    


    static SketchPad()
    {
      PropertyMetadata widthInternalMetaData = new PropertyMetadata(0.0, new PropertyChangedCallback(SomePropertyChanged));
      InternalWidthProperty = DependencyProperty.Register("InternalWidth", typeof(double), typeof(SketchPad), widthInternalMetaData);
      PropertyMetadata heightInternalMetaData = new PropertyMetadata(0.0, new PropertyChangedCallback(SomePropertyChanged));
      InternalHeightProperty = DependencyProperty.Register("InternalHeight", typeof(double), typeof(SketchPad), heightInternalMetaData);
    }

    public SketchPad()
    {
      this.DefaultDrawingAttributes.FitToCurve = false;
      this.DefaultDrawingAttributes.Width = 2;
      this.DefaultDrawingAttributes.Height = 2;

      this.EditingModeChanged += new RoutedEventHandler(WozObjectContainer_EditingModeChanged);

      // We bind our internal representation of the height and width properties
      // to our actual (WPF) version.
      Binding widthBinding = new Binding();
      widthBinding.Mode = BindingMode.OneWay;
      widthBinding.Source = this;
      widthBinding.Path = new PropertyPath(Canvas.WidthProperty);
      BindingOperations.SetBinding(this, InternalWidthProperty, widthBinding);

      Binding heightBinding = new Binding();
      heightBinding.Mode = BindingMode.OneWay;
      heightBinding.Source = this;
      heightBinding.Path = new PropertyPath(Canvas.HeightProperty);
      BindingOperations.SetBinding(this, InternalHeightProperty, heightBinding);

      // Setup Ink Canvas      
      WozObjectRegistry = new SketchObjectRegistry();
      WozObjectRegistry.ServerMode = true;


      _DefaultWozClientCommandReferences = new SketchCommandReferences();
      _DefaultWozClientCommandReferences.InkCanvas = this;
      _DefaultWozClientCommandReferences.WozObjectRegistry = this.WozObjectRegistry;


      //this.Background = Brushes.Transparent;

      //this.EditingMode = InkCanvasEditingMode.None;

      //this.DefaultDrawingAttributes.Width = 4;
      //this.DefaultDrawingAttributes.Height = 4;

      

      this.StrokeCollected += new InkCanvasStrokeCollectedEventHandler(ClientInkCanvas_StrokeCollected);
      this.SelectionMoved += new EventHandler(ClientInkCanvas_SelectionMoved);
      this.SelectionResized += new EventHandler(ClientInkCanvas_SelectionResized);
      this.SelectionChanging += new InkCanvasSelectionChangingEventHandler(ClientInkCanvas_SelectionChanging);
      this.SelectionChanged += new EventHandler(WozObjectContainer_SelectionChanged);

      this.PreviewKeyDown += new KeyEventHandler(WozObjectContainer_PreviewKeyDown);

      this.StrokeErasing += new InkCanvasStrokeErasingEventHandler(WozObjectContainer_StrokeErasing);

      this.PreviewMouseDown += new MouseButtonEventHandler(ClientInkCanvas_PreviewMouseDown);

      this.PreviewMouseUp += new MouseButtonEventHandler(ClientInkCanvas_PreviewMouseUp);
      //this.MouseUp += new MouseButtonEventHandler(ClientInkCanvas_PreviewMouseUp);

      this.MouseDown += new MouseButtonEventHandler(WozObjectContainer_MouseDown);

      this.MouseUp += new MouseButtonEventHandler(ClientInkCanvas_MouseUp);

      //this.MouseMove += new MouseEventHandler(ClientInkCanvas_MouseMove);
      this.PreviewMouseMove += new MouseEventHandler(ClientInkCanvas_MouseMove);

      //this.EditingMode = InkCanvasEditingMode.Select;
      this.SelectedInkTool = CurrentInkTool;

      CurrentInkToolChanged += new CurrentInkToolChangedDelegate(WozObjectContainer_CurrentInkToolChanged);


      this.Drop += new DragEventHandler(WozObjectContainer_Drop);
      
      this.AllowDrop = true;

      // MAY BE MORE APPROPRIATE TO DO THIS AT THE WINDOW LEVEL
      CommandBinding PasteToolCommandBinding = new CommandBinding(ApplicationCommands.Paste);
      PasteToolCommandBinding.CanExecute += new CanExecuteRoutedEventHandler(PasteToolCommandBinding_CanExecute);
      PasteToolCommandBinding.Executed += new ExecutedRoutedEventHandler(PasteToolCommandBinding_Executed);
      PasteToolCommandBinding.PreviewExecuted += new ExecutedRoutedEventHandler(PasteToolCommandBinding_PreviewExecuted);
      this.CommandBindings.Add(PasteToolCommandBinding);

      CommandBinding CopyToolCommandBinding = new CommandBinding(ApplicationCommands.Copy);
      CopyToolCommandBinding.CanExecute += new CanExecuteRoutedEventHandler(CopyToolCommandBinding_CanExecute);
      CopyToolCommandBinding.Executed += new ExecutedRoutedEventHandler(CopyToolCommandBinding_Executed);
      CopyToolCommandBinding.PreviewExecuted += new ExecutedRoutedEventHandler(CopyToolCommandBinding_PreviewExecuted);
      this.CommandBindings.Add(CopyToolCommandBinding);

      this.UserCurrentInkToolChanged += new UserCurrentInkToolChangedHandler(SketchPad_UserCurrentInkToolChanged);
      this.UserToolsEnabledCollectionChanged += new UserToolsEnabledCollectionChangedHandler(SketchPad_UserToolsEnabledCollectionChanged);
    }

    

    void SketchPad_UserToolsEnabledCollectionChanged(List<SketchPad.InkTools> newInkToolCollection, List<SketchPad.InkTools> previousInkToolCollection)
    {
      ModifyUserToolsEnabledCollection modifyUserToolsEnabledCollection = new ModifyUserToolsEnabledCollection(newInkToolCollection, previousInkToolCollection);

      NotifyCommandOccured(modifyUserToolsEnabledCollection);
    }

    void SketchPad_UserCurrentInkToolChanged(SketchPad.InkTools newInkTool, SketchPad.InkTools previousInkTool)
    {
      ModifyUserCurrentInkTool modifyUserCurrentInkTool = new ModifyUserCurrentInkTool(newInkTool, previousInkTool);

      NotifyCommandOccured(modifyUserCurrentInkTool);
    }

    void CopyToolCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      e.Handled = true;

      // umm, i need to copy to the clipboard now
      if (this.GetSelectedElements().Count > 0 || this.GetSelectedStrokes().Count > 0)
      {
        // get selected objects dummy
        List<SketchObject> wozObjects = this.GetSelectedWozObjectList();

        XmlSerializer mySerializer = new XmlSerializer(typeof(List<SketchObject>));

        // To write to a file, create a StreamWriter object.
        StringWriter sw = new StringWriter();
        mySerializer.Serialize(sw, wozObjects);
        sw.Close();

        Clipboard.SetData("WozObjectsXML", sw.ToString());        
      }
    }

    /// <summary>
    /// Trying to fix textbox select bug
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void CopyToolCommandBinding_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
    {
      CopyToolCommandBinding_Executed(sender, e);
    }

    private List<SketchObject> GetSelectedWozObjectList()
    {
      List<SketchObject> wozObjects = new List<SketchObject>();

      foreach (UIElement uie in this.GetSelectedElements())
      {
        if (uie is ISketchObject)
        {
          wozObjects.Add((uie as ISketchObject).GetWozObject());
        }
      }

      foreach (Stroke stroke in this.GetSelectedStrokes())
      {
        if (stroke is ISketchObject)
        {
          wozObjects.Add((stroke as ISketchObject).GetWozObject());
        }
      }

      return wozObjects;
    }

    // almost same as VisibleWozObjects
    private List<SketchObject> GetWozObjectList()
    {
      List<SketchObject> wozObjects = new List<SketchObject>();

      foreach (UIElement uie in this.Children)
      {
        if (uie is ISketchObject)
        {
          wozObjects.Add((uie as ISketchObject).GetWozObject());
        }
      }

      foreach (Stroke stroke in this.Strokes)
      {
        if (stroke is ISketchObject)
        {
          wozObjects.Add((stroke as ISketchObject).GetWozObject());
        }
      }

      return wozObjects;
    }

    void CopyToolCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      if (this.GetSelectedElements().Count > 0 || this.GetSelectedStrokes().Count > 0)
      {
        e.CanExecute = true;
      }
      else
      {
        e.CanExecute = false;
      }
    }

    public delegate void ReplacementPasteOccuredDelegate(ReplacementPad newReplacementPad);
    public event ReplacementPasteOccuredDelegate ReplacementPasteOccured;

    void PasteToolCommandBinding_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
    {
      e.Handled = true;

      try
      {
        // if there are object selected - really treat this as replacement
        // I KNOW, IKNOW.... it's really bad that this object knows about it's child the Replacement pad
        if (this.SelectedWozObjects != null && this.SelectedWozObjects.Count > 0)
        {
          ReplacementPad replacementPad = new ReplacementPad(true);

          // instantiate a ReplaceSketchCommand
          ReplacementSketchCommand replacementSketchCommand = new ReplacementSketchCommand(
            replacementPad, this, this.SelectedWozObjects);

          // set the references
          SketchCommandReferences sketchCommandReferences = new SketchCommandReferences();
          sketchCommandReferences.InkCanvas = this;
          sketchCommandReferences.WozObjectRegistry = this.WozObjectRegistry;
          replacementSketchCommand.WozClientCommandReferences = sketchCommandReferences;

          // redo it
          replacementSketchCommand.Redo();

          // log it & send to client
          this.NotifyCommandOccured(replacementSketchCommand);

          if (ReplacementPasteOccured != null)
          {
            // Need to pass this out as an event so it can be used
            // ReplacementPads.Add();
            ReplacementPasteOccured(replacementPad);
          }
        }
        else
        {

          List<UIElement> createdUies = new List<UIElement>();
          List<SketchObject> createdObjects = new List<SketchObject>();
          StrokeCollection createdStrokes = new StrokeCollection();

          // try to paste in WozObjectsXML
          if (Clipboard.ContainsData("WozObjectsXML"))
          {
            try
            {
              object o = Clipboard.GetData("WozObjectsXML");

              if (o is string)
              {
                string xml = o as string;

                // time to deserialize the XmlObjects
                // remember to register them as new objects (new wozIDs!)

                XmlSerializer mySerializer = new XmlSerializer(typeof(List<SketchObject>));

                StringReader sr = new StringReader(xml);

                object oUnserialized = mySerializer.Deserialize(sr);

                sr.Close();

                if (oUnserialized is List<SketchObject>)
                {
                  List<SketchObject> unserialized = oUnserialized as List<SketchObject>;

                  foreach (SketchObject wozObject in unserialized)
                  {
                    ISketchObject iWozObject = wozObject.GetNewIWozObject(this.WozObjectRegistry);

                    if (iWozObject is Stroke)
                    {
                      this.Strokes.Add(iWozObject as Stroke);
                      createdStrokes.Add(iWozObject as Stroke);
                    }
                    else if (iWozObject is UIElement)
                    {
                      this.Children.Add(iWozObject as UIElement);
                      createdUies.Add(iWozObject as UIElement);
                    }

                    // don't use original WozObject cause it has a bad ID
                    createdObjects.Add(iWozObject.GetWozObject());
                  }
                }
              }
            }
            catch (Exception ex)
            {
              Debug.WriteLine("Couldn't parse pasted xml: " + ex.ToString());
            }
          }
          else if (Clipboard.ContainsImage())
          {
            try
            {
              // get the imgage
              BitmapSource bs = Clipboard.GetImage();

              if (!Directory.Exists(Directory.GetCurrentDirectory() + "\\images"))
              {
                Directory.CreateDirectory(Directory.GetCurrentDirectory() + "\\images");
              }

              // $tss THIS IS TOTALLY WRONG
              // we need to find our app's directory instead of the currentdirectory - cause that changes all the time

              string filename = Directory.GetCurrentDirectory() +
                "\\images\\" + bs.GetHashCode().ToString() + ".png";

              // try to save it to disk
              // not very space efficient
              FileStream stream = new FileStream(filename, FileMode.Create);
              PngBitmapEncoder encoder = new PngBitmapEncoder();
              encoder.Frames.Add(BitmapFrame.Create(bs));
              encoder.Save(stream);
              stream.Close();

              Rect imageRect = new Rect(new Point(20, 20), new Size(bs.PixelWidth, bs.PixelHeight));

              // register the local file to be served
              HttpFileServer.RegisterFile(filename);
              //string remoteImageUrl = HttpFileServer.GetRemoteImageUrl(filename);
              string remoteImageUrl = filename;

              SketchRectangle wozRectangle = new SketchRectangle(imageRect, this.WozObjectRegistry, remoteImageUrl);
              this.Children.Add(wozRectangle);

              createdObjects.Add(wozRectangle.GetWozObject());

              createdUies.Add(wozRectangle);
            }
            catch (Exception ex)
            {
              Debug.WriteLine("Couldn't create image: " + ex.ToString());
            }
          }
          else if (Clipboard.ContainsText())
          {
            string text = Clipboard.GetText();

            // create a WozRectangle
            Rect rect = new Rect(15, 15, 400, 40);

            SketchText wozText = new SketchText(rect, this.WozObjectRegistry, text);
            this.Children.Add(wozText);

            createdObjects.Add(wozText.GetWozObject());

            createdUies.Add(wozText);

            wozText.TextChanged += new TextChangedEventHandler(wozText_TextChanged);
          }
          else if (Clipboard.ContainsFileDropList())
          {

            foreach (string filename in Clipboard.GetFileDropList())
            {
              try
              {
                BitmapSource bs = new BitmapImage(new Uri(filename));

                Rect imageRect = new Rect(new Point(20, 20), new Size(Width, Height));

                // register the local file to be served
                HttpFileServer.RegisterFile(filename);
                //string remoteImageUrl = HttpFileServer.GetRemoteImageUrl(filename);
                string remoteImageUrl = filename;

                SketchRectangle wozRectangle = new SketchRectangle(imageRect, this.WozObjectRegistry, remoteImageUrl);
                this.Children.Add(wozRectangle);

                createdObjects.Add(wozRectangle.GetWozObject());

                createdUies.Add(wozRectangle);
              }
              catch (Exception ex)
              {
                Debug.WriteLine("Couldn't create image: " + ex.ToString());
              }
            }
          }

          if (createdObjects.Count > 0 && (createdUies.Count > 0 || createdStrokes.Count > 0))
          {
            SelectToolWozCommand wstc = new SelectToolWozCommand(SketchPad.CurrentInkTool, InkTools.Selector);

            NotifyCommandOccured(wstc);

            this.Select(createdStrokes, createdUies);

            CreateSketchCommand wccc = new CreateSketchCommand(createdObjects);

            NotifyCommandOccured(wccc);
          }
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
    }

    void PasteToolCommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      // nothing to do here
    }

    void PasteToolCommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
    {
      e.CanExecute = true;
    }

    void WozObjectContainer_Drop(object sender, DragEventArgs e)
    {
      List<UIElement> createdUies = new List<UIElement>();
      List<SketchObject> createdObjects = new List<SketchObject>();

      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
        object o = e.Data.GetData(DataFormats.FileDrop);
        if (o is string[])
        {
          string[] filenames = o as string[];

          foreach (string filename in filenames)
          {
            try
            {
              BitmapSource bs = new BitmapImage(new Uri(filename));

              Rect imageRect = new Rect(e.GetPosition(this), new Size(bs.PixelWidth, bs.PixelHeight));

              // register the local file to be served
              HttpFileServer.RegisterFile(filename);
              //string remoteImageUrl = HttpFileServer.GetRemoteImageUrl(filename);
              string remoteImageUrl = filename;

              SketchRectangle wozRectangle = new SketchRectangle(imageRect, this.WozObjectRegistry, remoteImageUrl);
              this.Children.Add(wozRectangle);

              createdObjects.Add(wozRectangle.GetWozObject());

              createdUies.Add(wozRectangle);
            }
            catch (Exception ex)
            {
              Debug.WriteLine("Couldn't create image: " + ex.ToString());
            }
          }
        }
      }
      else
      {
        string[] formats = e.Data.GetFormats();
      }

      if (createdObjects.Count > 0 && createdUies.Count > 0)
      {
        SelectToolWozCommand wstc = new SelectToolWozCommand(SketchPad.CurrentInkTool, InkTools.Selector);

        NotifyCommandOccured(wstc);

        this.Select(createdUies);

        CreateSketchCommand wccc = new CreateSketchCommand(createdObjects);

        NotifyCommandOccured(wccc);
      }
    }

    void WozObjectContainer_PreviewKeyDown(object sender, KeyEventArgs e)
    {
      if (e.Key == Key.Delete)
      {
        e.Handled = true;

        List<SketchObject> toErase = new List<SketchObject>();
        foreach (UIElement uie in this.GetSelectedElements())
        {
          if (uie is ISketchObject)
          {
            ISketchObject iWozObject = uie as ISketchObject;
            toErase.Add(iWozObject.GetWozObject());
          }
        }
        foreach (Stroke stroke in this.GetSelectedStrokes())
        {
          if (stroke is ISketchObject)
          {
            ISketchObject iWozObject = stroke as ISketchObject;
            toErase.Add(iWozObject.GetWozObject());
          }
        }
        //WozEraseCommand
        if (toErase.Count > 0)
        {
          EraseSketchCommand wozEraseCommand = new EraseSketchCommand(toErase);
          wozEraseCommand.Redo();
          NotifyCommandOccured(wozEraseCommand, true);
        }
      }
    }

    void WozObjectContainer_EditingModeChanged(object sender, RoutedEventArgs e)
    {
      Console.WriteLine(this.EditingMode);
    }

    

    void WozObjectContainer_SelectionChanged(object sender, EventArgs e)
    {
      FirePropertyChanged("SelectWozObjectCascadingProperties");
    }

    //public void CommitChanges()
    //{
    //  lock (SinceCommitServerCommandStack)
    //  {
    //    SinceCommitServerCommandStack.Clear();
    //  }
    //}

    //public void DiscardChanged()
    //{
    //  lock (SinceCommitServerCommandStack)
    //  {
    //    while (SinceCommitServerCommandStack.Count > 0)
    //    {
    //      SinceCommitServerCommandStack.Pop().Undo();
    //    }
    //  }
    //}

    void WozObjectContainer_StrokeErasing(object sender, InkCanvasStrokeErasingEventArgs e)
    {
      if (e.Stroke is ISketchObject)
      {

        // create erase command
        EraseSketchCommand eraseCommand = new EraseSketchCommand((e.Stroke as ISketchObject).GetWozObject());

        // push erase command
        NotifyCommandOccured(eraseCommand);
      }
    }

    private void WozObjectContainer_CurrentInkToolChanged(SketchPad.InkTools currentInkTool)
    {
      SetInkTool(currentInkTool);
    }

    private void SetInkTool(InkTools currentInkTool)
    {
      SelectedInkTool = currentInkTool;
    }

    public void UpdateWithSketchPadServerObject(SketchPadServerObject wozObjectContainerServerObject)
    {
      this.ServerID = wozObjectContainerServerObject.ServerID;

      this.Description = wozObjectContainerServerObject.Description;

      this.Width = wozObjectContainerServerObject.Width;
      this.Height = wozObjectContainerServerObject.Height;

      this.UserCurrentInkTool = wozObjectContainerServerObject.UserTool;
      this.UserToolsEnabledCollection = wozObjectContainerServerObject.UserTools;

      if (wozObjectContainerServerObject._WozObjects != null)
      {
        foreach (SketchObject wozObject in wozObjectContainerServerObject._WozObjects)
        {
          ISketchObject iWozObject = wozObject.GetIWozObject(this.WozObjectRegistry);

          if (iWozObject is Stroke)
          {
            this.Strokes.Add(iWozObject as Stroke);
          }
          else if (iWozObject is UIElement)
          {
            this.Children.Add(iWozObject as UIElement);
          }
        }
      }
    }

    /// <summary>
    /// Clones a this sketch pad, copies it's width, height, and woz objects.
    /// It does not copy the ServerID and does not register it as a new ServerObject
    /// </summary>
    /// <returns></returns>
    public virtual SketchPad Clone()
    {
      // create a new sketch pad
      SketchPad clonedSketchPad = new SketchPad();

      // we won't even bother registering it, because we will never save it's state or anything

      clonedSketchPad.Width = this.Width;
      clonedSketchPad.Height = this.Height;

      // get every visible wozobject
      foreach (SketchObject wozObject in this.VisibleWozObjects)
      {
        // get them in reference to the cloned SketchPad's object space because
        // that way they will get recreated (cloned) and registered in the new SketchPad's space
        ISketchObject iWozObject = wozObject.GetIWozObject(clonedSketchPad.WozObjectRegistry);

        if (iWozObject is Stroke)
        {
          clonedSketchPad.Strokes.Add(iWozObject as Stroke);
        }
        else if (iWozObject is UIElement)
        {
          clonedSketchPad.Children.Add(iWozObject as UIElement);
        }
      }

      // we don't want any editing on this sketch pad
      clonedSketchPad.IgnoreInkTool = true;

      return clonedSketchPad;
    }

    #region IServerObject Members

    public long ServerID
    {
      get
      {
        return _ServerID;
      }
      set
      {
        _ServerID = value;
      }
    } long _ServerID;

    public virtual ServerObject GetServerObject()
    {
      return new SketchPadServerObject(this.ServerID);
    }

    #endregion

    void ClientInkCanvas_SelectionChanging(object sender, InkCanvasSelectionChangingEventArgs e)
    {
      try
      {
        lock (_PreviousSelectedWozObjects)
        {
          List<SketchObject> nextSelectedWozObjects = new List<SketchObject>();
          List<UIElement> nextSelectedUIElements = new List<UIElement>();
          StrokeCollection nextSelectedStrokes = new StrokeCollection();
          // make sure only "selectable" things get selected
          foreach (UIElement uie in e.GetSelectedElements())
          {
            if (uie is ISketchObject)
            {
              nextSelectedWozObjects.Add((uie as ISketchObject).GetWozObject());
              nextSelectedUIElements.Add(uie);
            }
            // $tss DEBUG ONLY
            else
            {
              nextSelectedUIElements.Add(uie);
            }
          }

          if (Keyboard.Modifiers == ModifierKeys.Control || Keyboard.Modifiers == ModifierKeys.Shift)
          {
            foreach (SketchObject wo in _PreviousSelectedWozObjects)
            {
              nextSelectedWozObjects.Add(wo);

              ISketchObject iwoz = wo.GetIWozObject(WozObjectRegistry);

              if (iwoz is UIElement)
              {
                UIElement uie = iwoz as UIElement;
                nextSelectedUIElements.Add(uie);
              }
              else if (iwoz is Stroke)
              {
                nextSelectedStrokes.Add(iwoz as Stroke);
              }
            }
          }

          foreach (Stroke stroke in e.GetSelectedStrokes())
          {
            if (stroke is ISketchObject)
            {
              nextSelectedWozObjects.Add((stroke as ISketchObject).GetWozObject());
              nextSelectedStrokes.Add(stroke);
            }
          }


          // ToDo: set what will be selected based on our list
          e.SetSelectedElements(nextSelectedUIElements);
          e.SetSelectedStrokes(nextSelectedStrokes);

          // create selection change command
          ChangeSelectionSketchCommand selectionCommand = new ChangeSelectionSketchCommand(nextSelectedWozObjects, _PreviousSelectedWozObjects);

          // push selection change command
          NotifyCommandOccured(selectionCommand);

          // reset our list
          _PreviousSelectedWozObjects = nextSelectedWozObjects;

          bool first = true;
          foreach (UIElement uie in nextSelectedUIElements)
          {
            if (uie is SketchText && first)
            {
              (uie as SketchText).TextBox.Focus();
              first = false;
            }
          }
        }
      }
      catch (Exception ex)
      {
        Debug.WriteLine(ex.ToString());
      }
    }

    void ClientInkCanvas_SelectionResized(object sender, EventArgs e)
    {
      SelectionModified();
    }

    void ClientInkCanvas_SelectionMoved(object sender, EventArgs e)
    {
      SelectionModified();
    }

    private void SelectionModified()
    {
      List<SketchObject> previousWozObject = new List<SketchObject>();
      List<SketchObject> nextWozObject = new List<SketchObject>();

      foreach (Stroke stroke in this.Strokes)
      {
        if (stroke is ISketchObject)
        {
          ISketchObject iWozObject = stroke as ISketchObject;

          // capture previous state
          previousWozObject.Add(iWozObject.GetWozObject());

          // sync state
          iWozObject.SyncProperties();

          //capture new state
          nextWozObject.Add(iWozObject.GetWozObject());
        }
      }
      foreach (UIElement uie in this.Children)
      {
        if (uie is ISketchObject)
        {
          ISketchObject iWozObject = uie as ISketchObject;

          // capture previous state
          previousWozObject.Add(iWozObject.GetWozObject());

          // sync state
          iWozObject.SyncProperties();

          //capture new state
          nextWozObject.Add(iWozObject.GetWozObject());
        }
      }

      // create modification command
      ModifyObjectSketchCommand modifyCommand = new ModifyObjectSketchCommand(nextWozObject, previousWozObject);

      // push modification command
      NotifyCommandOccured(modifyCommand);
    }

    void WozObjectContainer_MouseDown(object sender, MouseButtonEventArgs e)
    {
      
    }

    void ClientInkCanvas_MouseUp(object sender, MouseButtonEventArgs e)
    {
      
    }

    // Adorners for previewing shape drawing
    private LinePreviewAdorner _LinePreviewAdorner = null;
    private Point _MouseStartPoint;
    private RectanglePreviewAdorner _RectanglePreviewAdorner = null;
    private EllipsePreviewAdorner _EllipsePreviewAdorner = null;

    private SelectionPreviewAdorner _SelectionPreviewAdorner = null;
    private StrokeSelectionPreviewAdorner _StrokeSelectionPreviewAdorner = null;

    void ClientInkCanvas_MouseMove(object sender, MouseEventArgs e)
    {
      // determine if mouse button is down
      bool mouseOrPenDown = (e.LeftButton == MouseButtonState.Pressed || e.RightButton == MouseButtonState.Pressed ||
        e.MiddleButton == MouseButtonState.Pressed);

      // get the mouse position relative to the ink canvas
      Point pointRelativeToInkCanvas = e.GetPosition(this);
      MouseOrPenMove(pointRelativeToInkCanvas, mouseOrPenDown);
    }

    // could use this if we wanted to wrap all erasing in one stroke into a single erase command
    // personally i prefer not to
    //private List<WozObject> _ToErase = new List<WozObject>();

    bool _MouseIgnore = false;

    /// <summary>
    /// Mouse/Pen invarient move handler - Mouse or stylus onmove handlers should call this method
    /// </summary>
    /// <param name="pointRelativeToInkCanvas">the location of the mouse or stylus relative to the InkCanvas</param>
    /// <param name="mouseOrPenDown">should be true if any mouse button is pressed, the stylus is down, 
    /// or any barrel button is pressed</param>
    protected void MouseOrPenMove(Point pointRelativeToInkCanvas, bool mouseOrPenDown)
    {
      if (!_MouseIgnore)
      {
        _MouseIgnore = true;

        //if (mouseOrPenDown)
        //{
        //}


        if (!mouseOrPenDown)
        {
          switch (SelectedInkTool)
          {
            case InkTools.Selector:

              //if (_SelectionPreviewAdorner != null)
              //{
              //  AdornerLayer layer = AdornerLayer.GetAdornerLayer(this);
              //  if (layer.GetAdorners(this) != null)
              //  {
              //    try
              //    {
              //      //layer.Remove(_SelectionPreviewAdorner);
              //    }
              //    catch (Exception ex)
              //    {
              //      Debug.WriteLine(ex);
              //    }
              //  }
              //}

              //HitTestResult htr = this.HitTestCore(new PointHitTestParameters(pointRelativeToInkCanvas));

              //if (htr != null && htr is IWozObject)
              //{
              //  //htr.ToString
              //}

              //IInputElement iie = this.InputHitTest(pointRelativeToInkCanvas);
              //DependencyObject iie = htr.VisualHit;
              //if (iie != null && iie is UIElement && ! (iie is Adorner) && iie != this)
              //{
              //  foreach (UIElement uie in this.Children)
              //  {
              //    if (uie.IsAncestorOf(iie as UIElement))
              //    {
              //      if (!this.GetSelectedElements().Contains(uie))
              //      {

              //        _SelectionPreviewAdorner = new SelectionPreviewAdorner(this, uie);
              //        AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
              //        adornerLayer.Add(_SelectionPreviewAdorner);
              //        break;

              //      }
              //    }
              //  }
              //}

              //bool adornerValidated = false;
              UIElement uieToAdorn = null;
              Stroke strokeToAdorn = null;

              //IInputElement bob = this.InputHitTest(pointRelativeToInkCanvas);
              //if (bob is Adorner)
              //{
              //  Adorner bobby = bob as Adorner;

              //  IInputElement sally = bobby.InputHitTest(pointRelativeToInkCanvas);
              //  Size s = bobby.RenderSize;
              //  if (sally != bobby)
              //  {
              //  }
              //}

              //ReadOnlyCollection<UIElement> selectedElements = this.GetSelectedElements();
              //Rect outsideBox = new Rect(0.0, 0.0, 0.0, 0.0);
              //foreach (UIElement element in selectedElements)
              //{
              //  outsideBox.Union(
              //    new Rect(
              //    new Point(element.
              //}
              

              //if (!(bob is Adorner))
              if (true)
              {


                Stack<UIElement> stack = new Stack<UIElement>();
                foreach (UIElement uie in this.Children)
                {
                  stack.Push(uie);
                }
                foreach (UIElement uie in stack)
                {
                  IInputElement iie = 
                    uie.InputHitTest(this.TranslatePoint(pointRelativeToInkCanvas, uie));

                  if (iie is Adorner)
                  {
                  }

                  if (iie != null && (!(iie is Adorner)))
                  {
                    if (!this.GetSelectedElements().Contains(uie))
                    {

                      //if (_SelectionPreviewAdorner != null && 
                      //  _SelectionPreviewAdorner.SelectingElement != uie)
                      //{
                      //  AdornerLayer adornerLayer = 
                      //    AdornerLayer.GetAdornerLayer(_SelectionPreviewAdorner.SelectingElement);
                      //  if (adornerLayer != null)
                      //  {
                      //    adornerLayer.Remove(_SelectionPreviewAdorner);
                      //    _SelectionPreviewAdorner = null;
                      //  }
                      //}

                      //if (_SelectionPreviewAdorner == null)
                      //{
                      //  //_SelectionPreviewAdorner = new SelectionPreviewAdorner(this, uie);
                      //  //AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                      //  _SelectionPreviewAdorner = new SelectionPreviewAdorner(uie, uie);
                      //  AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(uie);
                      //  adornerLayer.Add(_SelectionPreviewAdorner);
                      //}

                      //adornerValidated = true;

                      uieToAdorn = uie;

                      break;

                    }
                    else
                    {
                      //if (_SelectionPreviewAdorner != null && 
                      //  _SelectionPreviewAdorner.SelectingElement != uie)
                      //{
                      //  AdornerLayer adornerLayer = 
                      //    AdornerLayer.GetAdornerLayer(_SelectionPreviewAdorner.SelectingElement);
                      //  if (adornerLayer != null)
                      //  {
                      //    adornerLayer.Remove(_SelectionPreviewAdorner);
                      //    _SelectionPreviewAdorner = null;
                      //  }
                      //}
                    }
                  }
                }
              }

              //if (!adornerValidated && _SelectionPreviewAdorner != null)
              //{
              //  AdornerLayer adornerLayer = 
              //    AdornerLayer.GetAdornerLayer(_SelectionPreviewAdorner.SelectingElement);
              //  if (adornerLayer != null)
              //  {
              //    adornerLayer.Remove(_SelectionPreviewAdorner);
              //    _SelectionPreviewAdorner = null;
              //  }
              //}

              // Try to allow selection of a stroke
              Stack<Stroke> strokeStack = new Stack<Stroke>();
              foreach (Stroke stroke in this.Strokes)
              {
                strokeStack.Push(stroke);
              }
              foreach (Stroke stroke in strokeStack)
              {
                if (stroke.HitTest(pointRelativeToInkCanvas) && !this.GetSelectedStrokes().Contains(stroke))
                {
                  // then we should actually select the stroke!
                  strokeToAdorn = stroke;
                  break;
                }

              }

              // lastly check InFrontOfInk
              // todo


              if (_SelectionPreviewAdorner != null && 
                  (uieToAdorn == null || _SelectionPreviewAdorner.SelectingElement != uieToAdorn ||
                  strokeToAdorn != null)
                )
              {
                try
                {
                  AdornerLayer adornerLayer =
                    AdornerLayer.GetAdornerLayer(_SelectionPreviewAdorner.SelectingElement);
                  if (adornerLayer != null)
                  {
                    adornerLayer.Remove(_SelectionPreviewAdorner);
                    _SelectionPreviewAdorner = null;
                  }
                }
                catch (Exception ex)
                {
                  Debug.WriteLine(ex.ToString());
                }
              }

              if (_StrokeSelectionPreviewAdorner != null &&
                  (strokeToAdorn == null || _StrokeSelectionPreviewAdorner.SelectingStroke != strokeToAdorn)
                )
              {
                try
                {
                  AdornerLayer adornerLayer =
                    AdornerLayer.GetAdornerLayer(this);
                  if (adornerLayer != null)
                  {
                    adornerLayer.Remove(_StrokeSelectionPreviewAdorner);
                    _StrokeSelectionPreviewAdorner = null;
                  }
                }
                catch (Exception ex)
                {
                  Debug.WriteLine(ex.ToString());
                }
              }

              // front to back priority
              if (strokeToAdorn != null && _StrokeSelectionPreviewAdorner == null)
              {
                _StrokeSelectionPreviewAdorner = new StrokeSelectionPreviewAdorner(this, strokeToAdorn);
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                adornerLayer.Add(_StrokeSelectionPreviewAdorner);
              }

              if (strokeToAdorn == null && uieToAdorn != null && _SelectionPreviewAdorner == null)
              {
                //_SelectionPreviewAdorner = new SelectionPreviewAdorner(this, uieToAdorn);
                //AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                _SelectionPreviewAdorner = new SelectionPreviewAdorner(uieToAdorn, uieToAdorn);
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(uieToAdorn);
                adornerLayer.Add(_SelectionPreviewAdorner);
              }

              break;

            default:
              break;
          }
        }

        if (mouseOrPenDown)
        {
          switch (SelectedInkTool)
          {
            case InkTools.Pencil:
              break;
            case InkTools.Eraser:
              // try to erase IWozObjects

              // the real trick is going to be wrapping this all up into a single erase command
              // with what the ink eraser does, maybe using mouse up?
              List<SketchObject> toErase = new List<SketchObject>();
              foreach (UIElement uie in this.Children)
              {
                if (uie is ISketchObject)
                {
                  ISketchObject iWozObject = uie as ISketchObject;
                  if (iWozObject.ShouldErase(this.TranslatePoint(pointRelativeToInkCanvas, uie)))
                  {
                    toErase.Add(iWozObject.GetWozObject());
                  }
                }
              }
              //WozEraseCommand
              if (toErase.Count > 0)
              {
                EraseSketchCommand wozEraseCommand = new EraseSketchCommand(toErase);
                wozEraseCommand.Redo();
                NotifyCommandOccured(wozEraseCommand, true);
              }
              break;
            case InkTools.Selector:
              break;
            case InkTools.Line:
              // update our preview of the line being created
              if (_LinePreviewAdorner != null)
              {
                _LinePreviewAdorner.EndPoint = pointRelativeToInkCanvas;
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Update(this);
                }
              }
              break;
            case InkTools.Image:
              goto case InkTools.Rectangle;
            case InkTools.Rectangle:
              if (_RectanglePreviewAdorner != null)
              {
                _RectanglePreviewAdorner.Rect = new Rect(_MouseStartPoint, pointRelativeToInkCanvas);
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Update(this);
                }
              }
              break;
            case InkTools.Ellipse:
              // update our preview of the Ellipse being created
              if (_EllipsePreviewAdorner != null)
              {
                _EllipsePreviewAdorner.EndPoint = pointRelativeToInkCanvas;
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Update(this);
                }
              }
              break;
            case InkTools.Text:
              goto case InkTools.Rectangle;
            case InkTools.Link:
              goto case InkTools.Rectangle;
            case InkTools.Button:
              goto case InkTools.Rectangle;
            case InkTools.Arrow:
              break;
            case InkTools.Gradient:
              goto case InkTools.Rectangle;
            default:
              break;
          }
        }
        _MouseIgnore = false;
      }
    }

    void ClientInkCanvas_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
      if (!_MouseIgnore)
      {
        _MouseIgnore = true;
        bool capturedMouse;

        if (e.RightButton == MouseButtonState.Pressed)
        {
          switch (SelectedInkTool)
          {
            case InkTools.Pencil:
              break;
            case InkTools.Eraser:
              break;
            case InkTools.Selector:
              //IInputElement iie = this.InputHitTest(e.GetPosition(this));
              //if (iie != null && iie is UIElement)
              //{
              //  foreach (UIElement uie in this.Children){
              //    if (uie.IsAncestorOf(iie as UIElement))
              //    {
              //      List<UIElement> uies = new List<UIElement>();
              //      uies.Add(uie);
              //      this.Select(uies);

              //      break;
              //    }
              //  }
              //}



              //Stack<UIElement> stack = new Stack<UIElement>();
              //foreach (UIElement uie in this.Children)
              //{
              //  stack.Push(uie);
              //};
              //List<UIElement> uies = new List<UIElement>();
              //foreach (UIElement uie in stack)
              //{
              //  IInputElement iie = uie.InputHitTest(this.TranslatePoint(e.GetPosition(this), uie));
              //  if (iie != null && !(iie is Adorner))
              //  {
              //    if (!this.GetSelectedElements().Contains(uie))
              //    {


              //      uies.Add(uie);


              //      break;

              //    }
              //    else
              //    {
              //      uies.Clear();
              //    }
              //  }
              //}
              //if (uies.Count > 0)
              //{
              //  this.Select(uies);
              //}

              if (e.RightButton == MouseButtonState.Pressed)
              {

                Stack<UIElement> stack = new Stack<UIElement>();
                List<UIElement> uies = new List<UIElement>();
                StrokeCollection strokes = new StrokeCollection();

                foreach (UIElement uie in this.Children)
                {
                  stack.Push(uie);
                }

                foreach (UIElement uie in stack)
                {
                  IInputElement iie =
                    uie.InputHitTest(this.TranslatePoint(e.GetPosition(this), uie));

                  if (iie != null && (!(iie is Adorner)))
                  {
                    if (!this.GetSelectedElements().Contains(uie))
                    {
                      uies.Add(uie);
                      break;
                    }
                  }
                }

                Stack<Stroke> strokeStack = new Stack<Stroke>();
                foreach (Stroke stroke in this.Strokes)
                {
                  strokeStack.Push(stroke);
                }
                foreach (Stroke stroke in strokeStack)
                {
                  if (stroke.HitTest(e.GetPosition(this)) && !this.GetSelectedStrokes().Contains(stroke))
                  {
                    // then we should actually select the stroke!
                    strokes.Add(stroke);
                    break;
                  }

                }

                if (strokes.Count > 0)
                {
                  this.Select(strokes);
                }
                else
                {
                  this.Select(uies);
                }

              }

              break;
            case InkTools.Line:
              break;
            case InkTools.Rectangle:
              break;
            case InkTools.Ellipse:
              break;
            case InkTools.Image:
              break;
            case InkTools.Text:
              break;
            case InkTools.Button:
              break;
            case InkTools.Arrow:
              break;
            case InkTools.Gradient:
              break;
            default:
              break;
          }
        }

        if (e.LeftButton == MouseButtonState.Pressed && e.RightButton == MouseButtonState.Released)
        {

          switch (SelectedInkTool)
          {
            case InkTools.Pencil:
              break;
            case InkTools.Eraser:
              break;
            case InkTools.Selector:

              break;
            case InkTools.Line:
              // might want to check that we succeed before doing preview
              capturedMouse = this.CaptureMouse();
              if (capturedMouse)
              {
                // setup what we need to create a line
                _LinePreviewAdorner = new LinePreviewAdorner(this, e.GetPosition(this), e.GetPosition(this));
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                adornerLayer.Add(_LinePreviewAdorner);
              }

              break;
            case InkTools.Image:
              goto case InkTools.Rectangle;
            case InkTools.Rectangle:
              capturedMouse = this.CaptureMouse();
              if (capturedMouse)
              {
                // setup what we need to create a line
                _RectanglePreviewAdorner = new RectanglePreviewAdorner(this, new Rect(e.GetPosition(this), e.GetPosition(this)));
                _MouseStartPoint = e.GetPosition(this);
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                adornerLayer.Add(_RectanglePreviewAdorner);
              }
              break;
            case InkTools.Gradient:
              goto case InkTools.Rectangle;
            case InkTools.Ellipse:
              // might want to check that we succeed before doing preview
              capturedMouse = this.CaptureMouse();
              if (capturedMouse)
              {
                // setup what we need to create a line
                _EllipsePreviewAdorner = new EllipsePreviewAdorner(this, e.GetPosition(this), e.GetPosition(this));
                AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                adornerLayer.Add(_EllipsePreviewAdorner);
              }
              break;
            case InkTools.Text:
              goto case InkTools.Rectangle;
            case InkTools.Link:
              goto case InkTools.Rectangle;
            case InkTools.Button:
              goto case InkTools.Rectangle;
            case InkTools.Arrow:
              break;
            default:
              break;
          }
        }

        _MouseIgnore = false;
      }
    }

    void ClientInkCanvas_PreviewMouseUp(object sender, MouseButtonEventArgs e)
    {
      UIElement createdUie = null;
      CreateSketchCommand creationCommand = null;

      if (!_MouseIgnore && Mouse.Captured == this)
      {
        _MouseIgnore = true;
        AdornerLayer adornerLayer;

        if (e.LeftButton == MouseButtonState.Released)
        {

          switch (SelectedInkTool)
          {
            case InkTools.Pencil:
              break;
            case InkTools.Eraser:
              break;
            case InkTools.Selector:
              break;
            case InkTools.Line:

              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_LinePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_LinePreviewAdorner);
                }

                // create a IWozLine
                SketchLine wozLine = new SketchLine(_LinePreviewAdorner.StartPoint, _LinePreviewAdorner.EndPoint, this.WozObjectRegistry);
                this.Children.Add(wozLine);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozLine.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                createdUie = wozLine;
              }
              break;
            case InkTools.Rectangle:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_RectanglePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

                // create a WozRectangle
                Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                SketchRectangle wozRectangle = new SketchRectangle(rect, this.WozObjectRegistry);
                this.Children.Add(wozRectangle);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozRectangle.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                createdUie = wozRectangle;
              }
              break;
            case InkTools.Link:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_RectanglePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

                // create a WozRectangle
                Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                SketchRectangle wozRectangle = new SketchRectangle(rect, this.WozObjectRegistry);

                // Add link information to rectangle
                wozRectangle.SetProperty(new ColorSketchProperty(Color.FromScRgb(0.0001f,1.0f,1.0f,1.0f), WozPropertyType.FillColor));
                wozRectangle.SetProperty(new ThicknessSketchProperty(new Thickness(0.0)));
                wozRectangle.SetProperty(new LongSketchProperty(-1,WozPropertyType.LinkedScreenID));

                this.Children.Add(wozRectangle);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozRectangle.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                createdUie = wozRectangle;
              }
              break;
            case InkTools.Gradient:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_RectanglePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

                // create a WozRectangle
                Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                SketchRectangle wozRectangle = new SketchRectangle(rect, this.WozObjectRegistry, null, true);
                this.Children.Add(wozRectangle);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozRectangle.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                createdUie = wozRectangle;
              }
              break;
            case InkTools.Ellipse:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_EllipsePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_EllipsePreviewAdorner);
                }

                // create a WozEllipse
                //Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                EllipseGeometry eg = new EllipseGeometry(_EllipsePreviewAdorner.StartPoint,
                  Math.Abs(_EllipsePreviewAdorner.StartPoint.X - _EllipsePreviewAdorner.EndPoint.X),
                  Math.Abs(_EllipsePreviewAdorner.StartPoint.Y - _EllipsePreviewAdorner.EndPoint.Y));
                SketchEllipse wozEllipse = new SketchEllipse(eg.Bounds, this.WozObjectRegistry);
                this.Children.Add(wozEllipse);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozEllipse.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                createdUie = wozEllipse;
              }
              break;
            case InkTools.Image:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);

              // have to create this before we launch the file dialog
              Rect imageRect = new Rect(_MouseStartPoint, e.GetPosition(this));

              if (_RectanglePreviewAdorner != null)
              {
                // get a an image file from the user to use
                OpenFileDialog dlg = new OpenFileDialog();
                dlg.Filter = "Image Files(*.BMP;*.JPG;*.JPEG;*.GIF;*.PNG)|*.BMP;*.JPG;*.JPEG;*.GIF;*.PNG|All files (*.*)|*.*";
                bool? result = dlg.ShowDialog();
                if (result == true && System.IO.File.Exists(dlg.FileName))
                {
                  try
                  {
                    // register the local file to be served
                    HttpFileServer.RegisterFile(dlg.FileName);
                    //string remoteImageUrl = HttpFileServer.GetRemoteImageUrl(dlg.FileName);
                    string remoteImageUrl = dlg.FileName;

                    SketchRectangle wozRectangle = new SketchRectangle(imageRect, this.WozObjectRegistry, remoteImageUrl);
                    this.Children.Add(wozRectangle);

                    // create CreateCommand
                    CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozRectangle.GetWozObject());

                    // push creation command
                    creationCommand = (wozClientCreateCommand);

                    createdUie = wozRectangle;
                  }
                  catch (Exception ex)
                  {
                    Debug.WriteLine("Couldn't create image: " + ex.ToString());
                  }

                }

                // remove this last so that you can see where you are adding it when using the
                // open file dialog
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

              }
              break;
            case InkTools.Text:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_RectanglePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

                // create a WozRectangle
                Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                SketchText wozText = new SketchText(rect, this.WozObjectRegistry);
                this.Children.Add(wozText);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozText.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                wozText.TextChanged += new TextChangedEventHandler(wozText_TextChanged);

                createdUie = wozText;

              }
              break;
            case InkTools.Button:
              // release the mouse 
              this.ReleaseMouseCapture();

              // remove the line preview
              adornerLayer = AdornerLayer.GetAdornerLayer(this);
              if (_RectanglePreviewAdorner != null)
              {
                if (adornerLayer.GetAdorners(this) != null)
                {
                  adornerLayer.Remove(_RectanglePreviewAdorner);
                }

                // create a WozRectangle
                Rect rect = new Rect(_MouseStartPoint, e.GetPosition(this));
                SketchButton sketchButton = new SketchButton(rect, this.WozObjectRegistry);
                this.Children.Add(sketchButton);

                // create CreateCommand
                CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(sketchButton.GetWozObject());

                // push creation command
                creationCommand = (wozClientCreateCommand);

                //wozText.TextChanged += new TextChangedEventHandler(wozText_TextChanged);

                createdUie = sketchButton;

              }
              break;
            case InkTools.Arrow:
              break;
            default:
              break;
          }
        }
        _MouseIgnore = false;
      }

      if (createdUie != null && creationCommand != null)
      {
        //WozObjectContainer.CurrentInkTool = InkTools.Selector;
        //wstc.Redo();

        SelectToolWozCommand wstc = new SelectToolWozCommand(SketchPad.CurrentInkTool, InkTools.Selector);
        
        NotifyNoUndoRedoCommandOccured(wstc);

        creationCommand.SelectToolWozCommand = wstc;

        //WozChangeSelectionCommand wsc = new WozChangeSelectionCommand(
        List<UIElement> uies = new List<UIElement>();
        uies.Add(createdUie);
        this.Select(uies);

        //hack
        // to make it so the wizard can start typing immediatly after typing text
        foreach (UIElement uie in uies)
        {
          if (uie is SketchText)
          {
            (uie as SketchText).TextBox.Focus();
          }
        }

        NotifyCommandOccured(creationCommand);
      }
    }

    

    public void wozText_TextChanged(object sender, TextChangedEventArgs e)
    {
      if (sender is SketchText)
      {
        SketchText wozText = sender as SketchText;

        List<SketchObject> previousWozObject = new List<SketchObject>();
        List<SketchObject> nextWozObject = new List<SketchObject>();

        // capture previous state
        previousWozObject.Add(wozText.GetWozObject());

        // sync state
        wozText.SyncProperties();

        //capture new state
        nextWozObject.Add(wozText.GetWozObject());

        // create modification command
        ModifyObjectSketchCommand modifyCommand = new ModifyObjectSketchCommand(nextWozObject, previousWozObject);

        // push modification command
        NotifyCommandOccured(modifyCommand);
      }
    }

    

    public delegate void CommandOccuredDelegate(WozCommand wozServerCommand);

    public event CommandOccuredDelegate CommandOccured;
    public event CommandOccuredDelegate CommandOccuredNoUndoRedo;
    public event CommandOccuredDelegate ContinuousCommandOccured;

    void ClientInkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
    {
      Stroke stroke = e.Stroke;

      // Replace the stroke with a path
      //ReplaceStrokeWithPath(stroke);

      // Replace the stroke with a WozStroke
      SketchStroke wozStroke = ReplaceStrokeWithWozStroke(stroke);

      // create CreateCommand
      CreateSketchCommand wozClientCreateCommand = new CreateSketchCommand(wozStroke.GetWozObject());

      // push creation command
      NotifyCommandOccured(wozClientCreateCommand);
    }

    public void NotifyCommandOccured(Ink.SketchCommands.SketchCommand sketchCommand)
    {
      NotifyCommandOccured(sketchCommand, false);
    }

    private void NotifyNoUndoRedoCommandOccured(WozCommand command)
    {
      if (CommandOccuredNoUndoRedo != null)
      {
        CommandOccuredNoUndoRedo(command);
      }
    }

    private void NotifyCommandOccured(Ink.SketchCommands.SketchCommand wozClientCreateCommand, bool shouldRedoIt)
    {
      if (!Suspended)
      {
        
        wozClientCreateCommand.WozClientCommandReferences = _DefaultWozClientCommandReferences;

        if (shouldRedoIt)
        {
          wozClientCreateCommand.Redo();
        }

        ClientWozCommand wozServerClientCommand = new ClientWozCommand(wozClientCreateCommand);

        SketchPadWozCommand wocsc = new SketchPadWozCommand(this.ServerID, wozServerClientCommand, true);

        if (CommandOccured != null)
        {
          CommandOccured(wocsc);
        }
      }
    }

    private void NotifyCommandOccured(WozCommand wozServerCommand)
    {
      SketchPadWozCommand wocsc = new SketchPadWozCommand(this.ServerID, wozServerCommand, true);

      if (CommandOccured != null)
      {
        CommandOccured(wocsc);
      }
    }

    SketchStroke ReplaceStrokeWithWozStroke(Stroke stroke)
    {
      // remove the stroke because we are going to create a a iwozobject out of it
      this.Strokes.Remove(stroke);

      // Create a path using the stroke's geometry
      SketchStroke wozStroke = new SketchStroke(stroke, this.WozObjectRegistry);

      // add the new path to our children
      this.Strokes.Add(wozStroke);

      return wozStroke;
    }

    void ReplaceStrokeWithPath(Stroke stroke)
    {
      // remove the stroke because we are going to create a path out of it
      this.Strokes.Remove(stroke);

      // Create a path using the stroke's geometry
      System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
      path.Data = stroke.GetGeometry();
      path.Fill = Brushes.Black;
      path.StrokeThickness = stroke.DrawingAttributes.Width;

      // add the new path to our children
      this.Children.Add(path);
    }

    // for use with showing "fake" ink
    private bool _InkingOverlay = false;
    private Stroke _InkingOverlayStroke;
    private DrawingAttributes _InkingOverlayDrawingAttributes;
    ClientMouseAdorner _ClientMouseAdorner;
    public override void Overlay(Point Position, MouseButtonState LeftButton, MouseButtonState MiddleButton, MouseButtonState RightButton)
    {
      AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);

      if (_ClientMouseAdorner == null)
      {
        _ClientMouseAdorner = new ClientMouseAdorner(this, new Point(Position.X, Position.Y), UserCurrentInkTool);
        adornerLayer = AdornerLayer.GetAdornerLayer(this);
        if (adornerLayer != null)
        {
          adornerLayer.Add(_ClientMouseAdorner);
        }
      }
      else if (adornerLayer != null && adornerLayer.GetAdorners(this) == null)
      {
        adornerLayer.Add(_ClientMouseAdorner);
      }
      else if (adornerLayer != null)
      {
        bool inThere = false;
        foreach (Adorner adorner in adornerLayer.GetAdorners(this))
        {
          if (adorner == _ClientMouseAdorner)
          {
            inThere = true;
            break;
          }
        }

        if (!inThere)
        {
          adornerLayer.Add(_ClientMouseAdorner);
        }
      }

      // "true ||" is used to not hide mouse cursor during drawing
      if (true || LeftButton == MouseButtonState.Released)
      {
        _ClientMouseAdorner.PlacementCenterPoint = new Point(Position.X, Position.Y);
        _ClientMouseAdorner.InkTool = UserCurrentInkTool;
        _ClientMouseAdorner.Visibility = Visibility.Visible;

        // we check AdornerLayer.GetAdornerLayer(this) for null because
        // when this is not the top screen there is no adorner to use for
        // drawing the mouse position
        if (adornerLayer != null && adornerLayer.GetAdorners(this) != null)
        {
          adornerLayer.Update(this);
        }
      }
      else
      {
        _ClientMouseAdorner.Visibility = Visibility.Hidden;

        // we check AdornerLayer.GetAdornerLayer(this) for null because
        // when this is not the top screen there is no adorner to use for
        // drawing the mouse position
        if (adornerLayer != null && adornerLayer.GetAdorners(this) != null)
        {
          adornerLayer.Update(this);
        }
      }

      // make sure the inking over lay attributes are instantiated
      if (_InkingOverlayDrawingAttributes == null)
      {
        _InkingOverlayDrawingAttributes = new DrawingAttributes();
        //_InkingOverlayDrawingAttributes.IsHighlighter = true;
      }

      // figure out what the ink should look like
      // !!! for effeciency maybe change this only once per mouse up / mouse down
      switch (UserCurrentInkTool)
      {
        case InkTools.Pencil:
          _InkingOverlayDrawingAttributes.Color = Colors.Black;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
        case InkTools.Eraser:
          _InkingOverlayDrawingAttributes.Color = Colors.Transparent;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
        case InkTools.Selector:
          _InkingOverlayDrawingAttributes.Color = Colors.Gray;
          _InkingOverlayDrawingAttributes.IsHighlighter = true;
          break;
        case InkTools.None:
          _InkingOverlayDrawingAttributes.Color = Colors.Transparent;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
          case InkTools.Gesture:
            _InkingOverlayDrawingAttributes.Color = Colors.Orange;
            _InkingOverlayDrawingAttributes.IsHighlighter = true;
            break;
        default:
          _InkingOverlayDrawingAttributes.Color = Colors.Black;
          _InkingOverlayDrawingAttributes.IsHighlighter = false;
          break;
      }

      lock (_InkingOverlayDrawingAttributes)
      {
        if (LeftButton == MouseButtonState.Pressed && !_InkingOverlay)
        {
          _InkingOverlay = true;

          StylusPointCollection stylusPointCollection = new StylusPointCollection();
          stylusPointCollection.Add(new StylusPoint(Position.X, Position.Y));
          _InkingOverlayStroke = new Stroke(stylusPointCollection, _InkingOverlayDrawingAttributes);
          this.Strokes.Add(_InkingOverlayStroke);
        }
        else if (LeftButton == MouseButtonState.Pressed)
        {
          this.Suspended = true;
          _InkingOverlayStroke.StylusPoints.Add(new StylusPoint(Position.X, Position.Y));
          this.Suspended = false;
          this.InvalidateVisual();
        }
        else if (LeftButton == MouseButtonState.Released && _InkingOverlay)
        {
          _InkingOverlay = false;
          this.Strokes.Remove(_InkingOverlayStroke);

          //*********************
          //* Gesture Collected *
          //*********************
          // HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK
          if (this.UserCurrentInkTool == InkTools.Gesture && GestureOccured != null)
          {
            GestureOccured(new SketchStroke(_InkingOverlayStroke));
          }
        }
      }
    }

    //horrid hack
    public delegate void GestureOccuredDelegate(SketchStroke stroke);
    public static event GestureOccuredDelegate GestureOccured;

    public ToolTip VisualToolTip
    {
      get
      {
        ToolTip toolTip = new ToolTip();
        toolTip.Placement = System.Windows.Controls.Primitives.PlacementMode.Top;
        toolTip.VerticalOffset = 0;
        toolTip.HorizontalOffset = 5;
        toolTip.BorderThickness = new Thickness(0);
        toolTip.Background = Brushes.Transparent;
        toolTip.Opacity = 1;

        Grid grid = new Grid();
        Rectangle rectangle = new Rectangle();
        //rectangle.Width = 500;
        //rectangle.Height = 200;
        rectangle.RadiusX = 13;
        rectangle.RadiusY = 13;
        rectangle.Stroke = Brushes.Black;
        rectangle.Fill = new SolidColorBrush(Color.FromArgb(150, 153, 204, 255));

        StackPanel stack2 = new StackPanel();
        stack2.Orientation = Orientation.Vertical;
        stack2.Margin = new Thickness(10);

        Rectangle rectangle2 = new Rectangle();
        rectangle2.Width = 300;
        rectangle2.Height = 300;
        rectangle2.Stretch = Stretch.Uniform;
        rectangle2.Margin = new Thickness(5);
        VisualBrush visualBrush = new VisualBrush(this);
        visualBrush.Stretch = Stretch.Uniform;
        rectangle2.Fill = visualBrush;
        stack2.Children.Add(rectangle2);

        TextBlock textBlock = new TextBlock();
        textBlock.FontFamily = new FontFamily("Verdana");
        textBlock.FontSize = 12;
        textBlock.TextWrapping = TextWrapping.Wrap;
        textBlock.MaxWidth = 120;
        Binding textblockBinding = new Binding();
        textblockBinding.Mode = BindingMode.OneWay;
        textblockBinding.Source = this;
        textblockBinding.Path = new PropertyPath("Description");
        BindingOperations.SetBinding(textBlock, TextBlock.TextProperty, textblockBinding);
        stack2.Children.Add(textBlock);

        grid.Children.Add(rectangle);
        grid.Children.Add(stack2);
        toolTip.Content = grid;
        return toolTip;
      }
    }

    public static IServerObject FromSketchPadServerObject(SketchPadServerObject sketchPadServerObject)
    {
      SketchPad sketchPad = new SketchPad();

      sketchPad.UpdateWithSketchPadServerObject(sketchPadServerObject);

      return sketchPad;
    }

    public virtual SketchPad Clone(IEnumerable<SketchObject> toReplace, SketchPad container)
    {
      return this.Clone();
    }
  }
}
